#pragma once


#include "Number.h"
#include "Tape.h"
#include "Algorithm.h"
#include "Addition.h"
#include "Subtraction.h"


#define __simulator_begin_	begin
#define __BEGIN_			__simulator_begin_ :
//#define __simulator_end_	end
#define __END_				//__simulator_end_ :


class MainFunction
{
	#define _PLUS		"+"
	#define _MINUS		"-"
	#define _RESTART	"r"
	#define _END		"e"

	Number* _x;
	Number* _y;
	std::string _operator;
	Number* _result;
	Addition* _add;
	Subtraction* _sub;


public :
	MainFunction( void ) : _x( 0 ), _y( 0 ), _operator( "" ), _result( 0 ), _add( 0 ), _sub( 0 ) {}
	~MainFunction( void ) {}	

	void _Begin( void );
	bool _Execute( unsigned long term );	
	bool _IsEnd( void );
	void _End( void );


private :
	template<typename type_name> type_name _InputData( std::string ment );
	bool _IsValidData( void );		
	Algorithm* SuitableAlgorithm( Addition& add, Subtraction& sub );
	bool _Running( Algorithm& algorithm, Tape& tape, unsigned long term );
	void _SetResult( std::string tape );
	void _ClearData( void );
	void _ClearScreen( void ) { system( "cls" ); }
};




#if ( NORMAL_MODE )

void MainFunction::_Begin( void )
{
	//! input x value
	_x = new Number( _InputData<relative_type>( "> input x : " ) );
	//! input y value
	_y = new Number( _InputData<relative_type>( "> input y : " ) );
	//! input operator
	_operator.assign( _InputData<std::string>( "> input operator : " ) );	
}

#else

void MainFunction::_Begin( void )
{
	//! input x value
	_x = new Number( _InputData<std::string>( "> input x : " ), NumberType::Decimal );
	//! input y value
	_y = new Number( _InputData<std::string>( "> input y : " ), NumberType::Decimal );
	//! input operator
	_operator.assign( _InputData<std::string>( "> input operator : " ) );
}

#endif


Algorithm* MainFunction::SuitableAlgorithm( Addition& add, Subtraction& sub )
{
	if( _PLUS == _operator )
	{
		return &add;
	}
	else if( _MINUS == _operator )
	{
		return &sub;
	}

	return 0;
}


#if ( NORMAL_MODE )

bool MainFunction::_Execute( unsigned long term )
{
	if( _IsValidData() )
	{		
		//! make tape
		Tape tape( _x->binary(), _y->binary(), _operator );
		//! algorithm
		Addition add( 0 );
		Subtraction sub( 0 );

		//! run simulator
		while( !_Running( *SuitableAlgorithm( add, sub ), tape, term ) );

		//! result
		_SetResult( tape.Data() );


		return true;
	}

	return false;
}

#else

bool MainFunction::_Execute( unsigned long term )
{
	if( _IsValidData() )
	{		
		//! make tape
		Tape tape( _x->binary(), _y->binary(), _operator );
		//! algorithm
		Addition add( 0 );
		Subtraction sub( 0 );

		//! run simulator
		while( !_Running( *SuitableAlgorithm( add, sub ), tape, term ) );

		//! result
		_SetResult( tape.Data() );


		return true;
	}

	return false;
}

#endif


void MainFunction::_End( void )
{
	_ClearData();
}


#if ( NORMAL_MODE )

bool MainFunction::_IsValidData( void )
{
	if( !( ( _x->decimal() >= _y->decimal() ) && ( _PLUS == _operator || _MINUS == _operator ) ) )
	{
		_ClearData();
		_ClearScreen();

		return false;
	}
	
	return true;
}

#else

bool MainFunction::_IsValidData( void )
{
	if( !( _PLUS == _operator || _MINUS == _operator ) )
	{
		_ClearData();
		_ClearScreen();

		return false;
	}

	return true;
}

#endif


template<typename type_name> type_name MainFunction::_InputData( std::string ment )
{
	type_name val;

	std::cout << ment;
	std::cin >> val;


	return val;
}


bool MainFunction::_Running( Algorithm& algorithm, Tape& tape, unsigned long term )
{
	bool is_end_scanning = algorithm.Run( tape.Data() );

	Sleep( term );			
	tape.Show( algorithm.GetState(), algorithm.GetHead(), algorithm.GetStep() );


	return is_end_scanning;
}


#if ( NORMAL_MODE )

void MainFunction::_SetResult( std::string tape )
{
	int result[MAX_BIT_SIZE];
	memset( &result, 0, sizeof( result ) );

	int index = MAX_BIT_SIZE - 1;


	for( size_t i = tape.size(); i != 0; --i )
	{	
		result[index] = atoi( tape.substr( i - 1, 1 ).c_str() );
		--index;
	}

	_result = new Number( result );

	std::cout << std::endl << "> x " << _operator << " y : " << _result->decimal() << " (decimal)" << std::endl;
}

#else

void MainFunction::_SetResult( std::string tape )
{
	_result = new Number( tape, NumberType::Binary );

	std::cout << std::endl << "> x " << _operator << " y : " << _result->binary() << " (binary)" << std::endl;
}

#endif


bool MainFunction::_IsEnd( void )
{
re_input :

	std::string input;
	
	std::cout << "> 'r'estart or 'e'nd ? : ";
	std::cin >> input;

	if( _RESTART == input )
	{
		_ClearData();
		_ClearScreen();

		return false;
	}
	else if( _END == input )
	{
		return true;
	}
	else
	{
		goto re_input;
	}


	return true;
}


void MainFunction::_ClearData( void )
{
	if( _x )
	{
		delete _x; _x = 0;
	}

	if( _y )
	{
		delete _y; _y = 0;
	}

	if( !_operator.empty() )
	{
		_operator.clear();
	}

	if( _result )
	{
		delete _result; _result = 0;
	}
}